[iOS] SwiftUIでお絵かきしたい
こんにちは。きんくまです。
SwiftUIでお絵かきしてみたいです。
今までFlashとかhtmlのCanvasとかで同じようなのを作ったことがあるので、SwiftUIでも作れるのか試してみました。
ビットマップではなくて、ベクターのお絵かきになります。
できたもの
Model
1ストロークごとの開始地点の座標と、線を結ぶための複数の座標があれば描けそうです。
ForEachで回したいのでIdentifiableに準拠するためにidを入れてあります。
struct DrawLine: Identifiable { static var idCount: Int = 0 var id: String var points: [CGPoint] static func makeDrawLine(points: [CGPoint]) -> DrawLine { let line = DrawLine(id: "\(DrawLine.idCount)", points: points) DrawLine.idCount += 1 return line } }
View
考え方としては、ドラッグをしながら座標を記録。ドラッグが終わったら、その座標をアーカイブとして保存。
描画も、ドラッグ中のものと、アーカイブしているものをそれぞれ分けて描画してあげれば、実装も簡単そうです。
struct ContentView: View { // すでに描いたLine @State private var lines: [DrawLine] = [] // いまドラッグ中のLine @State private var currentLine: DrawLine? var body: some View { VStack { // リセットボタン Button(action: { lines = [] }, label: { Text("Clear") }) ZStack { // Canvas部分 Rectangle() .fill(Color.white) .border(Color.black, width: 1) .gesture( DragGesture(minimumDistance: 0, coordinateSpace: .local) .onChanged({ value in if currentLine == nil { currentLine = DrawLine.makeDrawLine(points: []) } guard var line = currentLine else { return } line.points.append(value.location) currentLine = line }) .onEnded({ value in guard var line = currentLine else { return } line.points.append(value.location) lines.append(line) // リセット currentLine = nil }) ) // 追加ずみのLineの描画 ForEach(lines) { line in Path { path in path.addLines(line.points) }.stroke(Color.red, lineWidth: 1) }.clipped() // ドラッグ中のLineの描画 Path { path in guard let line = currentLine else { return } path.addLines(line.points) }.stroke(Color.red, lineWidth: 1) .clipped() }.padding(20) } } }
結構簡単に実装できました。
出てくる要望としては、とかでしょうか。
- 線ごとに色を変えたい
- 線ごとに太さを変えたい
- アンドゥを入れたい
1と2は、以下で実装できそうです。
- Modelに色と太さのプロパティを追加
- 色と太さの切り替えパネルを作る
- 切り替えパネルの状態を1ストロークごとに記録
- パスを描画するときに色と太さを反映
3は lines というプロパティの一番後ろデータを1つずつ削除していけばできますね。
まとめ
手軽に描画処理ができるのは良いなと思いました。
ではでは。